package org.convallaria.infrastruct.ce.util;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Component;

import java.util.Collection;
import java.util.concurrent.Callable;
import java.util.function.Supplier;

/**
 * 缓存工具类
 * 提供便捷的缓存操作方法
 * 
 * @author gjh
 * @since 2024-09-24
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class CacheUtils {

    private final CacheManager cacheManager;
    private final CacheKeyGenerator keyGenerator;

    /**
     * 获取缓存
     * 
     * @param cacheName 缓存名称
     * @param key 键
     * @return 缓存值
     */
    public <T> T get(String cacheName, String key, Class<T> type) {
        Cache cache = getCache(cacheName);
        if (cache == null) {
            return null;
        }
        
        Cache.ValueWrapper wrapper = cache.get(key);
        if (wrapper != null) {
            Object value = wrapper.get();
            if (value != null && type.isAssignableFrom(value.getClass())) {
                return type.cast(value);
            }
        }
        return null;
    }

    /**
     * 获取缓存，如果不存在则调用函数获取
     * 
     * @param cacheName 缓存名称
     * @param key 键
     * @param type 值类型
     * @param supplier 数据提供者
     * @return 缓存值
     */
    public <T> T getOrCompute(String cacheName, String key, Class<T> type, Supplier<T> supplier) {
        T value = get(cacheName, key, type);
        if (value != null) {
            return value;
        }
        
        T newValue = supplier.get();
        if (newValue != null) {
            put(cacheName, key, newValue);
        }
        return newValue;
    }

    /**
     * 获取缓存，如果不存在则调用Callable获取
     * 
     * @param cacheName 缓存名称
     * @param key 键
     * @param callable 数据提供者
     * @return 缓存值
     */
    public <T> T get(String cacheName, String key, Callable<T> callable) {
        Cache cache = getCache(cacheName);
        if (cache == null) {
            try {
                return callable.call();
            } catch (Exception e) {
                throw new RuntimeException("执行Callable失败", e);
            }
        }
        
        try {
            return cache.get(key, callable);
        } catch (Exception e) {
            log.warn("获取缓存失败，直接执行Callable: {}", key, e);
            try {
                return callable.call();
            } catch (Exception ex) {
                throw new RuntimeException("执行Callable失败", ex);
            }
        }
    }

    /**
     * 放入缓存
     * 
     * @param cacheName 缓存名称
     * @param key 键
     * @param value 值
     */
    public void put(String cacheName, String key, Object value) {
        Cache cache = getCache(cacheName);
        if (cache != null) {
            cache.put(key, value);
        }
    }

    /**
     * 移除缓存
     * 
     * @param cacheName 缓存名称
     * @param key 键
     */
    public void evict(String cacheName, String key) {
        Cache cache = getCache(cacheName);
        if (cache != null) {
            cache.evict(key);
        }
    }

    /**
     * 清除缓存
     * 
     * @param cacheName 缓存名称
     */
    public void clear(String cacheName) {
        Cache cache = getCache(cacheName);
        if (cache != null) {
            cache.clear();
        }
    }

    /**
     * 清除所有缓存
     */
    public void clearAll() {
        Collection<String> cacheNames = cacheManager.getCacheNames();
        for (String cacheName : cacheNames) {
            clear(cacheName);
        }
    }

    /**
     * 检查缓存是否存在
     * 
     * @param cacheName 缓存名称
     * @param key 键
     * @return 是否存在
     */
    public boolean exists(String cacheName, String key) {
        Cache cache = getCache(cacheName);
        if (cache == null) {
            return false;
        }
        return cache.get(key) != null;
    }

    /**
     * 获取缓存统计信息
     * 
     * @param cacheName 缓存名称
     * @return 统计信息
     */
    public CacheStatistics getStatistics(String cacheName) {
        Cache cache = getCache(cacheName);
        if (cache == null) {
            return CacheStatistics.empty();
        }
        
        // 这里可以根据具体的缓存实现获取统计信息
        // 例如：Caffeine的stats()方法
        return CacheStatistics.builder()
                .cacheName(cacheName)
                .size(getCacheSize(cache))
                .build();
    }

    /**
     * 生成缓存键
     * 
     * @param methodName 方法名
     * @param params 参数
     * @return 缓存键
     */
    public String generateKey(String methodName, Object... params) {
        return keyGenerator.generateKey(methodName, params);
    }

    /**
     * 生成带前缀的缓存键
     * 
     * @param prefix 前缀
     * @param methodName 方法名
     * @param params 参数
     * @return 缓存键
     */
    public String generateKey(String prefix, String methodName, Object... params) {
        return keyGenerator.generateKey(prefix, methodName, params);
    }

    /**
     * 获取缓存
     * 
     * @param cacheName 缓存名称
     * @return 缓存对象
     */
    private Cache getCache(String cacheName) {
        try {
            return cacheManager.getCache(cacheName);
        } catch (Exception e) {
            log.warn("获取缓存失败: {}", cacheName, e);
            return null;
        }
    }

    /**
     * 获取缓存大小（估算）
     * 
     * @param cache 缓存对象
     * @return 缓存大小
     */
    private long getCacheSize(Cache cache) {
        try {
            Object nativeCache = cache.getNativeCache();
            
            // 对于Caffeine缓存
            if (nativeCache instanceof com.github.benmanes.caffeine.cache.Cache) {
                com.github.benmanes.caffeine.cache.Cache<?, ?> caffeineCache = 
                    (com.github.benmanes.caffeine.cache.Cache<?, ?>) nativeCache;
                return caffeineCache.estimatedSize();
            }
            
            // 对于Redis缓存，这里简化处理
            // 实际项目中可以通过Redis命令获取更准确的大小
            if (nativeCache.getClass().getName().contains("redis")) {
                // 这里可以调用Redis的DBSIZE命令
                return 0; // 暂时返回0，实际实现需要Redis连接
            }
            
            // 其他缓存类型，尝试通过反射获取size方法
            try {
                java.lang.reflect.Method sizeMethod = nativeCache.getClass().getMethod("size");
                Object size = sizeMethod.invoke(nativeCache);
                if (size instanceof Number) {
                    return ((Number) size).longValue();
                }
            } catch (Exception e) {
                log.debug("无法获取缓存大小: {}", e.getMessage());
            }
            
        } catch (Exception e) {
            log.warn("获取缓存大小失败", e);
        }
        
        return 0;
    }

    /**
     * 缓存统计信息
     */
    @lombok.Data
    @lombok.Builder
    public static class CacheStatistics {
        private String cacheName;
        private long size;
        private long hits;
        private long misses;
        private double hitRatio;

        public static CacheStatistics empty() {
            return CacheStatistics.builder().build();
        }
    }
}
